#include "File.h"
#include "Buffer.h"
#include "FileIdHandler.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>

#ifndef NULL
	#define NULL 0
#endif

#ifndef ZERO
	#define ZERO 0
#endif

#ifndef UNO
	#define UNO 1
#endif

#define STATUS_ERROR 0
#define STATUS_CLOSE -1
#define STATUS_OPEN -2

File::File()
	:filename(NULL),
	handler(ZERO),
	status(STATUS_CLOSE)
{
}

File::~File()
{
	if(filename != NULL)	
	{
		delete []filename;
		filename=NULL;
	}
	if(status == STATUS_OPEN)
		this->close();
	
}

int File::open(const char* name)
{
	FileIdHandler* fileHandler = FileIdHandler::GetInstance();
	unsigned int size = strlen(name);
	if(name != NULL)
	{
		switch(status)
		{
			case STATUS_ERROR: status = STATUS_ERROR; break;
			case STATUS_OPEN: status = STATUS_ERROR; break;
			case STATUS_CLOSE:
				{
					
					if(filename != NULL)
						delete []filename;
					filename = new char[size + UNO];
					memcpy(filename, name, size);
					filename[size]='\0';

					// Trata de abrir el archivo para lectura-escritura
					// sino puede lo crea y lo intenta abrir.
					if( (handler = ::open(name, O_RDWR, 0644)) < ZERO)
					{						
						if( (handler = ::open(name, O_CREAT | O_RDWR, 0644)) < ZERO)
						{
							handler = ZERO;
							status = STATUS_ERROR;
						}
						else
						{

							::close(handler);
							// Trato de abrir el archivo para lectura-escritura
							if( (handler = ::open(name, O_RDWR, 0644)) < ZERO)
							{
								handler = ZERO;
								status = STATUS_ERROR;
							}
							else
								status = STATUS_OPEN;
						}
					}
					else
					{
						status = STATUS_OPEN;
					}
					if(status == STATUS_OPEN)
					{
						if(fileHandler != NULL)
							fileHandler->addHandler((char*)name);
					}
					
				}break;
		};
	}
	else
	{
	
		handler = ZERO;
		status = STATUS_ERROR;
	}

	if(status == STATUS_OPEN)
		return F_OK;
	else
		return F_ERROR;
}

int File::close()
{
	if(handler != ZERO)
	{
		switch(status)
		{
			case STATUS_ERROR: status = STATUS_ERROR; break;
			case STATUS_OPEN:
				{
					// Trato de cerrar el archivo
					if(::close(handler) < ZERO)
					{
						handler = ZERO;
						status = STATUS_ERROR;
					}
					else
					{
						status = STATUS_CLOSE;
						if(filename != NULL)
							delete []filename;
						filename = NULL;
					}
					
				} break;
			case STATUS_CLOSE: status = STATUS_ERROR; break;
		};
	}
	else
	{
		handler = ZERO;
		status = STATUS_ERROR;
	}

	if(status == STATUS_CLOSE)
		return F_OK;
	else
		return F_ERROR;
}

int File::write(void* ptr, unsigned int size)
{
	Buffer* buffer = NULL;
	FileIdHandler* fileHandler = NULL;
	int handlerAux = ZERO;

	if(status == STATUS_OPEN && handler != ZERO && ptr != NULL && size != ZERO)
	{
		// Lo agrego al buffer
		fileHandler = FileIdHandler::GetInstance();
		handlerAux = fileHandler->getHandler(filename);
		buffer = Buffer::GetInstance();
		if(handlerAux != F_ERROR)
			buffer->add(handlerAux, this->getCurrentPos(), ptr, size);
		// Lo escribo en disco
		if(::write(handler, ptr, size) >= ZERO)
			return F_OK;
	}
	status = STATUS_ERROR;
	return F_ERROR;
}

int File::read(void* ptr, unsigned int size)
{
	int operation = ZERO;
	unsigned int sizeBuffer = ZERO;
	Buffer* buffer = NULL;
	int handlerAux = ZERO;
	FileIdHandler* fileHandler = NULL;
	

	if(status == STATUS_OPEN && handler != ZERO && ptr != NULL && size != ZERO)
	{
		// Chequeo si se encuentra en el buffer
		fileHandler = FileIdHandler::GetInstance();
		handlerAux = fileHandler->getHandler(filename);
		buffer = Buffer::GetInstance();
		if(handlerAux != F_ERROR)
		{
			sizeBuffer=size;
			if(buffer->get(handlerAux,getSize(filename) , ptr, sizeBuffer) == F_OK)
				if( size == sizeBuffer)
					return F_OK;
		}
		
		memset(ptr,0,size);
		operation = ::read(handler, ptr, size);
			
		if(operation >= ZERO)
		{
			if(operation == ZERO)
			{
				return F_EOF;
			}
			else
			{
				return F_OK;
			}
			
		}
	}
	status = STATUS_ERROR;
	return F_ERROR;
}

int File::read(void* ptr, unsigned int size, int offset)
{
	int operation = ZERO;
	unsigned int sizeBuffer = ZERO;
	Buffer* buffer = NULL;
	int handlerAux = ZERO;
	FileIdHandler* fileHandler = NULL;


	if(status == STATUS_OPEN && handler != ZERO && ptr != NULL && size != ZERO)
	{
		// Chequeo si se encuentra en el buffer
		fileHandler = FileIdHandler::GetInstance();
		handlerAux = fileHandler->getHandler(filename);
		buffer = Buffer::GetInstance();
		if(handlerAux != F_ERROR)
		{
			sizeBuffer=size;
			if(buffer->get(handlerAux,offset , ptr, sizeBuffer) == F_OK)
				if( size == sizeBuffer)
				{
					return F_OK;
				}
		}
		
		memset(ptr,0,size);
		operation = ::read(handler, ptr, size);	
			
		if(operation >= ZERO)
		{
			if(operation == ZERO)
			{
				return F_EOF;
			}
			else
			{
				return F_OK;
			}
			
		}	
	}
	status = STATUS_ERROR;
	return F_ERROR;
}


int File::getCurrentPos()
{
	int pos = ZERO;
	
	if(status == STATUS_OPEN && handler != ZERO)
	{
		pos = lseek(handler, ZERO, SEEK_CUR);
		if(pos >= ZERO)
			return pos;
	}
	status = STATUS_ERROR;
	return F_ERROR;
}

int File::setCurrentPos(int posicion)
{
	int pos = ZERO;
	if(status == STATUS_OPEN && handler != ZERO)
	{
		pos = lseek(handler, posicion, SEEK_SET);
		if(pos >= ZERO)
		{	
			return F_OK;
		}
	}
	status = STATUS_ERROR;
	return F_ERROR;
}

int File::getSize(char *name)
{
	struct stat buf;

	if( lstat(name, &buf) < ZERO)
	{
		status = STATUS_ERROR;
		return F_ERROR;
	}
	else
		return buf.st_size;
}

bool File::exists(char *name)
{
	if(name != NULL)
	{
		if(this->getSize(name) != F_ERROR)
			return true;
		else
		{
			status = STATUS_CLOSE;
			return false;
		}
	}
	return false;
	
}

void File::eraseFile(char *name)
{
	if(name != NULL)
	{
		if(filename != NULL)
			if(strlen(name) == strlen(filename) && strncmp(name, filename, strlen(name)) == ZERO && status == STATUS_OPEN)
				this->close();
		remove(name);
	}
}





